
/*
    Revolution Engine
    Copyright (C) 2009 Felipe Apablaza

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


/**
 *
 *  File: Matrix2.hpp
 *  Desc: Two-Order matrix class template (OpenGL-Compliant)
 *
 */

#ifndef __REVENG_MATH_MATRIX2_HPP__
#define __REVENG_MATH_MATRIX2_HPP__

#include "Base.hpp"

namespace RevEng
{
    namespace Math
    {
        template<typename T>
		struct Matrix2
		{
            T M11, M21;
            T M12, M22;
			
			Matrix2<T>();
			Matrix2<T>(const T* data);
			Matrix2<T>( T m11, T m12,
                        T m21, T m22);
			
			Matrix2<T>& Inverse(T Det);
			Matrix2<T>& Inverse();
			Matrix2<T>& Transpose();
			
			T GetDeterminant() const;
			
			bool operator == (const Matrix2<T> &M) const;
			bool operator != (const Matrix2<T> &M) const;

			Matrix2<T> operator+ (const Matrix2<T> &M)const;
			Matrix2<T> operator- (const Matrix2<T> &M)const;
			Matrix2<T> operator- () const;
			Matrix2<T> operator* (const Matrix2<T> &M)const;
			Matrix2<T> operator* (const T &S)const;
			Matrix2<T> operator/ (const T &S)const;
			
			Matrix2<T>& operator+= (const Matrix2<T> &M);
			Matrix2<T>& operator-= (const Matrix2<T> &M);
			Matrix2<T>& operator*= (const Matrix2<T> &M);
			Matrix2<T>& operator*= (const T &S);
			Matrix2<T>& operator/= (const T &S);
			
			friend Matrix2<T> operator* (T S, const Matrix2<T> &M)
			{
			    return M*S;
			}
			
			operator T* ();
			operator T* () const;
			operator const T* ();
			operator const T* () const;
		};
		
		
        template<typename T> 
		Matrix2<T>::Matrix2() : 
            M11(static_cast<T>(0)), M21(static_cast<T>(0)),
            M12(static_cast<T>(0)), M22(static_cast<T>(0))
		{
		}


		template<typename T> 
		Matrix2<T>::Matrix2(const T* data)
		{
			M11 = data[0];
			M21 = data[1];
            M12 = data[2];
            M22 = data[3];
		}


		template<typename T> 
		Matrix2<T>::Matrix2(T m11, T m12, T m21, T m22) :	
			M11(m11), 
			M21(m21), 
			M12(m12), 
			M22(m22)
		{
		}

		
		template<typename T> 
		Matrix2<T>& Matrix2<T>::Inverse()
		{
			return this->Inverse( GetDeterminant() );
		}
		
		
		template<typename T> 
		Matrix2<T>& Matrix2<T>::Inverse(T Det)
		{
		    std::swap(M11, M22);
		    
		    M12 = -M12;
		    M21 = -M21;
		    
		    *this /= Det;
		    
			return *this;
		}
		
		
		template<typename T> 
		Matrix2<T>& Matrix2<T>::Transpose()
		{
		    std::swap(M12, M21);
		    
			return *this;
		}


		template<typename T> 
		T Matrix2<T>::GetDeterminant() const
		{
			return M11*M22 - M12*M21;
		}		
		
		template<typename T> 
		bool Matrix2<T>::operator == (const Matrix2<T> &M) const
		{
		    if (Equals<T>(M11, M.M11) == false)
                return false;
                
            if (Equals<T>(M21, M.M21) == false)
                return false;
                
            if (Equals<T>(M12, M.M12) == false)
                return false;
            
            if (Equals<T>(M22, M.M22) == false)
                return false;
                
            return true;
		}
		
		
		template<typename T> 
		bool Matrix2<T>::operator != (const Matrix2<T> &M) const
		{
		    return !(*this == M);
		}
		
		
        template<typename T> 
		Matrix2<T> Matrix2<T>::operator+ (const Matrix2<T> &M) const
		{
			return Matrix2<T>
			(
				M11 + M.M11, M12 + M.M12,
				M21 + M.M21, M22 + M.M22
			);
		}


		template<typename T> 
		Matrix2<T> Matrix2<T>::operator- (const Matrix2<T> &M) const
		{
			return Matrix2<T>
			(
				M11 - M.M11, M12 - M.M12,
				M21 - M.M21, M22 - M.M22
			);
		}


		template<typename T> 
		Matrix2<T> Matrix2<T>::operator- () const
		{
			return Matrix2<T>
			(
				-M11, -M21,
				-M12, -M22
			);
		}


		template<typename T> 
		Matrix2<T> Matrix2<T>::operator* (const Matrix2<T> &M)const
		{
			return Matrix2<T>
			(
				M11*M.M11 + M12*M.M21,	M11*M.M12 + M12*M.M22,
				M21*M.M11 + M22*M.M21,	M21*M.M12 + M22*M.M22
			);
		}


		template<typename T> 
		Matrix2<T> Matrix2<T>::operator* (const T &S)const
		{
			return Matrix2<T>
			(
				M11*S, M21*S,
				M12*S, M22*S
			);
		}


		template<typename T> 
		Matrix2<T> Matrix2<T>::operator/ (const T &S)const
		{
			return Matrix2<T>
			(
				M11/S, M12/S,
				M21/S, M22/S
			);
		}


		template<typename T> 
		Matrix2<T>& Matrix2<T>::operator+= (const Matrix2<T> &M)
		{
		    M11 += M.M11;
		    M21 += M.M21;
		    M12 += M.M12;
		    M22 += M.M22;
		    
			return *this;
		}


		template<typename T> 
		Matrix2<T>& Matrix2<T>::operator-= (const Matrix2<T> &M)
		{
		    M11 -= M.M11;
		    M21 -= M.M21;
		    M12 -= M.M12;
		    M22 -= M.M22;
		    
			return *this;
		}


		template<typename T> 
		Matrix2<T>& Matrix2<T>::operator*= (const Matrix2<T> &M)
		{
		    Matrix2<T> Result 
		    (
                M11*M.M11 + M12*M.M21, M11*M.M12 + M12*M.M22,
                M21*M.M11 + M22*M.M21, M21*M.M12 + M22*M.M22
		    );
		    
			return *this = Result;
		}


		template<typename T> 
		Matrix2<T>& Matrix2<T>::operator*= (const T &S)
		{
		    M11 *= S;
		    M21 *= S;
		    M12 *= S;
		    M22 *= S;
		    
			return *this;
		}


		template<typename T> 
		Matrix2<T>& Matrix2<T>::operator/= (const T &S)
		{
		    T InvS = static_cast<T>(1)/S;
		    
		    M11 *= InvS;
		    M21 *= InvS;
		    M12 *= InvS;
		    M22 *= InvS;
		    
			return *this;
		}
		
		
		template<typename T> 
        Matrix2<T>::operator T* ()
        {
            return &M11;
        }
        
        
        template<typename T> 
		Matrix2<T>::operator T* () const
		{
		    return &M11;
		}
		
		
		template<typename T> 
		Matrix2<T>::operator const T* ()
		{
		    return &M11;
		}
		
		
		template<typename T> 
		Matrix2<T>::operator const T* () const
		{
		    return &M11;
		}
    }
}

#endif
